/*
REWRITTEN FROM SCRATCH BY PUSSYWIZARD, IT OWNS NOW!
*/

#include "ObjectMgr.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellAuraEffects.h"
#include "icecrown_citadel.h"
#include "Player.h"

enum Texts
{
	// Blood Queen Lana'Thel
	SAY_INTRO_1                 = 0,
	SAY_INTRO_2                 = 1,

	// Prince Keleseth
	SAY_KELESETH_INVOCATION     = 0,
	EMOTE_KELESETH_INVOCATION   = 1,
	SAY_KELESETH_SPECIAL        = 2,
	SAY_KELESETH_KILL           = 3,
	EMOTE_KELESETH_BERSERK      = 4,
	SAY_KELESETH_DEATH          = 5,

	// Prince Taldaram
	SAY_TALDARAM_INVOCATION     = 0,
	EMOTE_TALDARAM_INVOCATION   = 1,
	SAY_TALDARAM_SPECIAL        = 2,
	EMOTE_TALDARAM_FLAME        = 3,
	SAY_TALDARAM_KILL           = 4,
	EMOTE_TALDARAM_BERSERK      = 5,
	EMOTE_TALDARAM_DEATH        = 6,

	// Prince Valanar
	SAY_VALANAR_INVOCATION      = 0,
	EMOTE_VALANAR_INVOCATION    = 1,
	SAY_VALANAR_SPECIAL         = 2,
	EMOTE_VALANAR_SHOCK_VORTEX  = 3,
	SAY_VALANAR_KILL            = 4,
	SAY_VALANAR_BERSERK         = 5,
	SAY_VALANAR_DEATH           = 6,
};

enum Spells
{
	SPELL_FEIGN_DEATH                   = 71598,
	SPELL_OOC_INVOCATION_VISUAL         = 70934,
	SPELL_INVOCATION_VISUAL_ACTIVE      = 71596,
	SPELL_INVOCATION_OF_BLOOD_KELESETH  = 70981,
	SPELL_INVOCATION_OF_BLOOD_TALDARAM  = 70982,
	SPELL_INVOCATION_OF_BLOOD_VALANAR   = 70952,

	// Heroic mode
	SPELL_SHADOW_PRISON                 = 72998,
	SPELL_SHADOW_PRISON_DAMAGE          = 72999,
	SPELL_SHADOW_PRISON_DUMMY           = 73001,

	// Prince Keleseth
	SPELL_SHADOW_RESONANCE              = 71943,
	SPELL_SHADOW_LANCE                  = 71405,
	SPELL_EMPOWERED_SHADOW_LANCE        = 71815,

	// Dark Nucleus
	SPELL_SHADOW_RESONANCE_AURA         = 72980,
	SPELL_SHADOW_RESONANCE_RESIST       = 71822,

	// Prince Taldaram
	SPELL_GLITTERING_SPARKS             = 71806,
	SPELL_CONJURE_FLAME                 = 71718,
	SPELL_CONJURE_EMPOWERED_FLAME       = 72040,

	// Ball of Flame
	SPELL_FLAME_SPHERE_SPAWN_EFFECT     = 55891, // cast from creature_template_addon (needed cast before entering world)
	SPELL_BALL_OF_FLAMES_VISUAL         = 71706,
	SPELL_BALL_OF_FLAMES                = 71714,
	SPELL_FLAMES                        = 71393,
	SPELL_FLAME_SPHERE_DEATH_EFFECT     = 55947,

	// Ball of Inferno Flame
	SPELL_BALL_OF_FLAMES_PROC           = 71756,
	SPELL_BALL_OF_FLAMES_PERIODIC       = 71709,

	// Prince Valanar
	SPELL_KINETIC_BOMB_TARGET           = 72053,
	SPELL_KINETIC_BOMB                  = 72080,
	SPELL_SHOCK_VORTEX                  = 72037,
	SPELL_EMPOWERED_SHOCK_VORTEX        = 72039,

	// Kinetic Bomb
	SPELL_UNSTABLE                      = 72059,
	SPELL_KINETIC_BOMB_VISUAL           = 72054,
	SPELL_KINETIC_BOMB_EXPLOSION        = 72052,
	SPELL_KINETIC_BOMB_KNOCKBACK        = 72087,

	// Shock Vortex
	SPELL_SHOCK_VORTEX_PERIODIC         = 71945,
	SPELL_SHOCK_VORTEX_DUMMY            = 72633,
};

enum Events
{
	EVENT_NONE,
	EVENT_INTRO_1,
	EVENT_INTRO_2,

	EVENT_INVOCATION_OF_BLOOD,
	EVENT_BERSERK,

	// Keleseth
	EVENT_SHADOW_RESONANCE,

	// Taldaram
	EVENT_GLITTERING_SPARKS,
	EVENT_CONJURE_FLAME,

	// Valanar
	EVENT_KINETIC_BOMB,
	EVENT_SHOCK_VORTEX,
	EVENT_BOMB_DESPAWN,
	EVENT_CONTINUE_FALLING,
};

enum Actions
{
	ACTION_STAND_UP             = 1,
	ACTION_CAST_INVOCATION      = 2,
	ACTION_REMOVE_INVOCATION    = 3,
	ACTION_FLAME_BALL_CHASE     = 4,
	ACTION_KINETIC_BOMB_JUMP    = 5,
};

enum Points
{
	POINT_INTRO_DESPAWN         = 380040,
};

class StandUpEvent : public BasicEvent
{
	public:
		StandUpEvent(Creature& owner) : BasicEvent(), _owner(owner) { }

		bool Execute(uint64 /*eventTime*/, uint32 /*diff*/)
		{
			_owner.HandleEmoteCommand(EMOTE_ONESHOT_ROAR);
			return true;
		}

	private:
		Creature& _owner;
};

class ShockVortexExplodeEvent : public BasicEvent
{
	public:
		ShockVortexExplodeEvent(Creature& owner) : BasicEvent(), _owner(owner) { }

		bool Execute(uint64 /*eventTime*/, uint32 /*diff*/)
		{
			_owner.CastSpell(&_owner, SPELL_SHOCK_VORTEX_PERIODIC, true);
			return true;
		}

	private:
		Creature& _owner;
};

Position const introFinalPos = {4660.490f, 2769.200f, 430.0000f, 0.000000f};
Position const triggerPos    = {4680.231f, 2769.134f, 379.9256f, 3.121708f};
Position const triggerEndPos = {4680.180f, 2769.150f, 365.5000f, 3.121708f};

class boss_prince_keleseth_icc : public CreatureScript
{
	public:
		boss_prince_keleseth_icc() : CreatureScript("boss_prince_keleseth_icc") { }

		struct boss_prince_kelesethAI : public ScriptedAI
		{
			boss_prince_kelesethAI(Creature* creature) : ScriptedAI(creature), summons(creature), instance(creature->GetInstanceScript())
			{
				if (!instance)
				{
					me->IsAIEnabled = false;
					me->AddUnitState(UNIT_STATE_EVADE);
				}
				_canDie = true;
			}

			void InitializeAI()
			{
				ScriptedAI::InitializeAI();
				if (me->IsAlive())
				{
					if (Creature* c = me->SummonCreature(WORLD_TRIGGER, me->GetHomePosition()))
					{
						c->SetObjectScale(1.75f);
						c->CastSpell(c, SPELL_FEIGN_DEATH, true);
					}

					me->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD);
					me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_IMMUNE_TO_NPC);
					me->SetReactState(REACT_PASSIVE);
				}
			}

			EventMap events;
			SummonList summons;
			InstanceScript* instance;
			bool _isEmpowered;
			bool _evading;
			bool _canDie;

			void Reset()
			{
				events.Reset();
				summons.DespawnAll();
				_isEmpowered = false;
				_evading = false;
				me->SetHealth(me->GetMaxHealth());
				me->SetReactState(REACT_AGGRESSIVE);
			}

			void EnterCombat(Unit* who)
			{
				bool valid = true;
				if (Creature* keleseth = instance->instance->GetCreature(instance->GetData64(DATA_PRINCE_KELESETH_GUID)))
					if (!keleseth->IsAlive() || keleseth->IsInEvadeMode())
						valid = false;
				if (Creature* taldaram = instance->instance->GetCreature(instance->GetData64(DATA_PRINCE_TALDARAM_GUID)))
					if (!taldaram->IsAlive() || taldaram->IsInEvadeMode())
						valid = false;
				if (Creature* valanar = instance->instance->GetCreature(instance->GetData64(DATA_PRINCE_VALANAR_GUID)))
					if (!valanar->IsAlive() || valanar->IsInEvadeMode())
						valid = false;
				if (!valid)
				{
					EnterEvadeMode();
					return;
				}

				me->RemoveAurasDueToSpell(SPELL_FEIGN_DEATH); // just in case
				me->setActive(true);
				DoZoneInCombat();
				if (!me->hasLootRecipient())
					me->SetLootRecipient(who);
				me->LowerPlayerDamageReq(me->GetMaxHealth());
				me->SetReactState(REACT_AGGRESSIVE);
				instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me);

				if (Creature* taldaram = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_PRINCE_TALDARAM_GUID)))
					if (!taldaram->IsInEvadeMode())
						taldaram->SetInCombatWithZone();
				if (Creature* valanar = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_PRINCE_VALANAR_GUID)))
					if (!valanar->IsInEvadeMode())
						valanar->SetInCombatWithZone();

				me->resetAttackTimer(BASE_ATTACK);
				DoAction(ACTION_REMOVE_INVOCATION);
				events.Reset();
				events.ScheduleEvent(EVENT_BERSERK, 600000);
				events.ScheduleEvent(EVENT_SHADOW_RESONANCE, urand(10000, 15000));
				if (IsHeroic())
					me->AddAura(SPELL_SHADOW_PRISON, me);
			}

			void AttackStart(Unit* who)
			{
				ScriptedAI::AttackStartCaster(who, 10.0f);
			}

			void JustDied(Unit* /*killer*/)
			{
				events.Reset();
				summons.DespawnAll();
				instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);

				if (!_canDie)
				{
					me->RemoveCorpse(false);
					me->SetRespawnTime(10);
					me->SaveRespawnTime();
					Position homePos = me->GetHomePosition();
					me->UpdatePosition(homePos);
					me->GetMotionMaster()->Clear(false);
					me->GetMotionMaster()->MoveIdle();
					me->StopMovingOnCurrentPos();
					return;
				}

				Talk(SAY_KELESETH_DEATH);
				if (Creature* taldaram = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_PRINCE_TALDARAM_GUID)))
					if (taldaram->IsAlive())
						Unit::Kill(taldaram, taldaram);
				if (Creature* valanar = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_PRINCE_VALANAR_GUID)))
					if (valanar->IsAlive())
						Unit::Kill(valanar, valanar);
			}

			void JustRespawned()
			{
				ScriptedAI::JustRespawned();
				JustReachedHome();
			}

			void JustReachedHome()
			{
				_canDie = true;
				me->setActive(false);
				instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
				DoAction(ACTION_REMOVE_INVOCATION);
				me->SetHealth(1);
			}

			void JustSummoned(Creature* summon)
			{
				summons.Summon(summon);
			}

			void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType, SpellSchoolMask)
			{
				if (!_isEmpowered)
				{
					if (attacker)
						me->AddThreat(attacker, float(damage));
					damage = 0;
				}
			}

			void DamageDealt(Unit* target, uint32& damage, DamageEffectType damageType)
			{
				if (target->GetTypeId() != TYPEID_PLAYER)
					return;

				if (damage > RAID_MODE<uint32>(23000, 25000, 23000, 25000))
					instance->SetData(DATA_ORB_WHISPERER_ACHIEVEMENT, 0);
			}

			void KilledUnit(Unit* victim)
			{
				if (victim->GetTypeId() == TYPEID_PLAYER)
					Talk(SAY_KELESETH_KILL);
			}

			void SpellHit(Unit* /*caster*/, SpellInfo const* spell)
			{
				if (spell->Id == 71080 && me->IsInCombat() && !me->IsInEvadeMode())
					DoAction(ACTION_CAST_INVOCATION);
			}

			void DoAction(int32 action)
			{
				switch (action)
				{
					case ACTION_STAND_UP:
						summons.DespawnEntry(WORLD_TRIGGER);
						me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_29 | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC);
						me->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD);
						me->SetReactState(REACT_AGGRESSIVE);
						me->ForceValuesUpdateAtIndex(UNIT_NPC_FLAGS);   // was in sniff. don't ask why
						me->m_Events.AddEvent(new StandUpEvent(*me), me->m_Events.CalculateTime(1000));
						DoAction(ACTION_REMOVE_INVOCATION);
						me->SetHealth(1);
						break;
					case ACTION_CAST_INVOCATION:
						Talk(SAY_KELESETH_INVOCATION);
						me->CastSpell(me, SPELL_INVOCATION_OF_BLOOD_KELESETH, true);
						me->CastSpell(me, SPELL_INVOCATION_VISUAL_ACTIVE, true);
						_isEmpowered = true;
						break;
					case ACTION_REMOVE_INVOCATION:
						_isEmpowered = false;
						me->RemoveAurasDueToSpell(SPELL_INVOCATION_VISUAL_ACTIVE);
						me->RemoveAurasDueToSpell(SPELL_INVOCATION_OF_BLOOD_KELESETH);
						break;
					default:
						break;
				}
			}

			void UpdateAI(uint32 diff)
			{
				if (!UpdateVictim())
					return;

				events.Update(diff);

				if (me->HasUnitState(UNIT_STATE_CASTING))
					return;

				switch (events.ExecuteEvent())
				{
					case EVENT_BERSERK:
						me->CastSpell(me, SPELL_BERSERK, true);
						Talk(EMOTE_KELESETH_BERSERK);
						break;
					case EVENT_SHADOW_RESONANCE:
						Talk(SAY_KELESETH_SPECIAL);
						me->CastSpell(me, SPELL_SHADOW_RESONANCE, false);
						events.ScheduleEvent(EVENT_SHADOW_RESONANCE, urand(10000, 15000));
						break;
				}

				DoSpellAttackIfReady(_isEmpowered ? SPELL_EMPOWERED_SHADOW_LANCE : SPELL_SHADOW_LANCE);
			}

			void EnterEvadeMode()
			{
				if (_evading)
					return;
				_canDie = false;
				me->SetHealth(me->GetMaxHealth());
				DoAction(ACTION_REMOVE_INVOCATION);
				_evading = true;
				if (Creature* taldaram = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_PRINCE_TALDARAM_GUID)))
					taldaram->AI()->EnterEvadeMode();
				if (Creature* valanar = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_PRINCE_VALANAR_GUID)))
					valanar->AI()->EnterEvadeMode();
				ScriptedAI::EnterEvadeMode();
				_evading = false;
			}
		};

		CreatureAI* GetAI(Creature* creature) const
		{
			return GetIcecrownCitadelAI<boss_prince_kelesethAI>(creature);
		}
};

class boss_prince_taldaram_icc : public CreatureScript
{
	public:
		boss_prince_taldaram_icc() : CreatureScript("boss_prince_taldaram_icc") { }

		struct boss_prince_taldaramAI : public ScriptedAI
		{
			boss_prince_taldaramAI(Creature* creature) : ScriptedAI(creature), summons(creature), instance(creature->GetInstanceScript())
			{
				if (!instance)
				{
					me->IsAIEnabled = false;
					me->AddUnitState(UNIT_STATE_EVADE);
				}
				_canDie = true;
			}

			void InitializeAI()
			{
				ScriptedAI::InitializeAI();
				if (me->IsAlive())
				{
					if (Creature* c = me->SummonCreature(WORLD_TRIGGER, me->GetHomePosition()))
					{
						c->SetObjectScale(1.75f);
						c->CastSpell(c, SPELL_FEIGN_DEATH, true);
					}

					me->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD);
					me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_IMMUNE_TO_NPC);
					me->SetReactState(REACT_PASSIVE);
				}
			}

			EventMap events;
			SummonList summons;
			InstanceScript* instance;
			bool _isEmpowered;
			bool _evading;
			bool _canDie;

			void Reset()
			{
				events.Reset();
				summons.DespawnAll();
				_isEmpowered = false;
				_evading = false;
				me->SetHealth(me->GetMaxHealth());
				me->SetReactState(REACT_AGGRESSIVE);
			}

			void EnterCombat(Unit* who)
			{
				bool valid = true;
				if (Creature* keleseth = instance->instance->GetCreature(instance->GetData64(DATA_PRINCE_KELESETH_GUID)))
					if (!keleseth->IsAlive() || keleseth->IsInEvadeMode())
						valid = false;
				if (Creature* taldaram = instance->instance->GetCreature(instance->GetData64(DATA_PRINCE_TALDARAM_GUID)))
					if (!taldaram->IsAlive() || taldaram->IsInEvadeMode())
						valid = false;
				if (Creature* valanar = instance->instance->GetCreature(instance->GetData64(DATA_PRINCE_VALANAR_GUID)))
					if (!valanar->IsAlive() || valanar->IsInEvadeMode())
						valid = false;
				if (!valid)
				{
					EnterEvadeMode();
					return;
				}

				me->RemoveAurasDueToSpell(SPELL_FEIGN_DEATH); // just in case
				me->setActive(true);
				DoZoneInCombat();
				if (!me->hasLootRecipient())
					me->SetLootRecipient(who);
				me->LowerPlayerDamageReq(me->GetMaxHealth());
				me->SetReactState(REACT_AGGRESSIVE);
				instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me);

				if (Creature* keleseth = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_PRINCE_KELESETH_GUID)))
					if (!keleseth->IsInEvadeMode())
						keleseth->SetInCombatWithZone();
				if (Creature* valanar = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_PRINCE_VALANAR_GUID)))
					if (!valanar->IsInEvadeMode())
						valanar->SetInCombatWithZone();

				DoAction(ACTION_REMOVE_INVOCATION);
				events.Reset();
				events.ScheduleEvent(EVENT_BERSERK, 600000);
				events.ScheduleEvent(EVENT_GLITTERING_SPARKS, urand(12000, 15000));
				events.ScheduleEvent(EVENT_CONJURE_FLAME, 20000);
				if (IsHeroic())
					me->AddAura(SPELL_SHADOW_PRISON, me);
			}

			void JustDied(Unit* /*killer*/)
			{
				events.Reset();
				summons.DespawnAll();
				instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);

				if (!_canDie)
				{
					me->RemoveCorpse(false);
					me->SetRespawnTime(10);
					me->SaveRespawnTime();
					Position homePos = me->GetHomePosition();
					me->UpdatePosition(homePos);
					me->GetMotionMaster()->Clear(false);
					me->GetMotionMaster()->MoveIdle();
					me->StopMovingOnCurrentPos();
					return;
				}

				Talk(EMOTE_TALDARAM_DEATH);
				if (Creature* keleseth = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_PRINCE_KELESETH_GUID)))
					if (keleseth->IsAlive())
						Unit::Kill(keleseth, keleseth);
				if (Creature* valanar = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_PRINCE_VALANAR_GUID)))
					if (valanar->IsAlive())
						Unit::Kill(valanar, valanar);
			}

			void JustRespawned()
			{
				ScriptedAI::JustRespawned();
				JustReachedHome();
			}

			void JustReachedHome()
			{
				_canDie = true;
				me->setActive(false);
				instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
				DoAction(ACTION_REMOVE_INVOCATION);
				me->SetHealth(1);
			}

			void JustSummoned(Creature* summon)
			{
				summons.Summon(summon);

				Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, -10.0f, true);
				if (!target)
					target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true);
				if (target)
				{
					if (summon->GetEntry() == NPC_BALL_OF_INFERNO_FLAME)
						Talk(EMOTE_TALDARAM_FLAME, target);
					summon->AI()->SetGUID(target->GetGUID());
				}
			}

			void SummonedCreatureDespawn(Creature* s)
			{
				summons.Despawn(s);
			}

			void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType, SpellSchoolMask)
			{
				if (!_isEmpowered)
				{
					if (attacker)
						me->AddThreat(attacker, float(damage));
					damage = 0;
				}
			}

			void DamageDealt(Unit* target, uint32& damage, DamageEffectType damageType)
			{
				if (target->GetTypeId() != TYPEID_PLAYER)
					return;

				if (damage > RAID_MODE<uint32>(23000, 25000, 23000, 25000))
					instance->SetData(DATA_ORB_WHISPERER_ACHIEVEMENT, 0);
			}

			void KilledUnit(Unit* victim)
			{
				if (victim->GetTypeId() == TYPEID_PLAYER)
					Talk(SAY_TALDARAM_KILL);
			}

			void SpellHit(Unit* /*caster*/, SpellInfo const* spell)
			{
				if (spell->Id == 71081 && me->IsInCombat() && !me->IsInEvadeMode())
					DoAction(ACTION_CAST_INVOCATION);
			}

			void DoAction(int32 action)
			{
				switch (action)
				{
					case ACTION_STAND_UP:
						summons.DespawnEntry(WORLD_TRIGGER);
						me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_29 | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC);
						me->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD);
						me->SetReactState(REACT_AGGRESSIVE);
						me->ForceValuesUpdateAtIndex(UNIT_NPC_FLAGS);   // was in sniff. don't ask why
						me->m_Events.AddEvent(new StandUpEvent(*me), me->m_Events.CalculateTime(1000));
						DoAction(ACTION_REMOVE_INVOCATION);
						me->SetHealth(1);
						break;
					case ACTION_CAST_INVOCATION:
						Talk(SAY_TALDARAM_INVOCATION);
						me->CastSpell(me, SPELL_INVOCATION_OF_BLOOD_TALDARAM, true);
						me->CastSpell(me, SPELL_INVOCATION_VISUAL_ACTIVE, true);
						_isEmpowered = true;
						break;
					case ACTION_REMOVE_INVOCATION:
						_isEmpowered = false;
						me->RemoveAurasDueToSpell(SPELL_INVOCATION_VISUAL_ACTIVE);
						me->RemoveAurasDueToSpell(SPELL_INVOCATION_OF_BLOOD_TALDARAM);
						break;
					case ACTION_FLAME_BALL_CHASE:
						summons.DoAction(ACTION_FLAME_BALL_CHASE, 1);
						break;
					default:
						break;
				}
			}

			void UpdateAI(uint32 diff)
			{
				if (!UpdateVictim())
					return;

				events.Update(diff);

				if (me->HasUnitState(UNIT_STATE_CASTING))
					return;

				switch (events.ExecuteEvent())
				{
					case EVENT_BERSERK:
						me->CastSpell(me, SPELL_BERSERK, true);
						Talk(EMOTE_TALDARAM_BERSERK);
						break;
					case EVENT_GLITTERING_SPARKS:
						me->CastSpell(me->GetVictim(), SPELL_GLITTERING_SPARKS, false);
						events.ScheduleEvent(EVENT_GLITTERING_SPARKS, urand(15000, 25000));
						break;
					case EVENT_CONJURE_FLAME:
						if (_isEmpowered)
						{
							me->CastSpell(me, SPELL_CONJURE_EMPOWERED_FLAME, false);
							events.ScheduleEvent(EVENT_CONJURE_FLAME, 15000);
						}
						else
						{
							me->CastSpell(me, SPELL_CONJURE_FLAME, false);
							events.ScheduleEvent(EVENT_CONJURE_FLAME, urand(20000, 25000));
						}
						Talk(SAY_TALDARAM_SPECIAL);
						break;
				}

				DoMeleeAttackIfReady();
			}

			void EnterEvadeMode()
			{
				if (_evading)
					return;
				_canDie = false;
				me->SetHealth(me->GetMaxHealth());
				DoAction(ACTION_REMOVE_INVOCATION);
				_evading = true;
				if (Creature* keleseth = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_PRINCE_KELESETH_GUID)))
					keleseth->AI()->EnterEvadeMode();
				if (Creature* valanar = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_PRINCE_VALANAR_GUID)))
					valanar->AI()->EnterEvadeMode();
				ScriptedAI::EnterEvadeMode();
				_evading = false;
			}
		};

		CreatureAI* GetAI(Creature* creature) const
		{
			return GetIcecrownCitadelAI<boss_prince_taldaramAI>(creature);
		}
};

class boss_prince_valanar_icc : public CreatureScript
{
	public:
		boss_prince_valanar_icc() : CreatureScript("boss_prince_valanar_icc") { }

		struct boss_prince_valanarAI : public BossAI
		{
			boss_prince_valanarAI(Creature* creature) : BossAI(creature, DATA_BLOOD_PRINCE_COUNCIL)
			{
				if (!instance)
				{
					me->IsAIEnabled = false;
					me->AddUnitState(UNIT_STATE_EVADE);
				}
				_canDie = true;
			}

			void InitializeAI()
			{
				ScriptedAI::InitializeAI();
				if (me->IsAlive())
				{
					if (Creature* c = me->SummonCreature(WORLD_TRIGGER, me->GetHomePosition()))
					{
						c->SetObjectScale(1.75f);
						c->CastSpell(c, SPELL_FEIGN_DEATH, true);
					}

					me->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD);
					me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_IMMUNE_TO_NPC);
					me->SetReactState(REACT_PASSIVE);
				}
			}

			bool _isEmpowered;
			bool _evading;
			bool _canDie;
			uint32 invocationOrder[3];
			uint8 currentInvocationIndex;

			void Reset()
			{
				events.Reset();
				summons.DespawnAll();
				_isEmpowered = false;
				_evading = false;
				me->SetHealth(me->GetMaxHealth());
				me->SetReactState(REACT_AGGRESSIVE);
				instance->SetBossState(DATA_BLOOD_PRINCE_COUNCIL, NOT_STARTED);
			}

			void EnterCombat(Unit* who)
			{
				bool valid = true;
				if (Creature* keleseth = instance->instance->GetCreature(instance->GetData64(DATA_PRINCE_KELESETH_GUID)))
					if (!keleseth->IsAlive() || keleseth->IsInEvadeMode())
						valid = false;
				if (Creature* taldaram = instance->instance->GetCreature(instance->GetData64(DATA_PRINCE_TALDARAM_GUID)))
					if (!taldaram->IsAlive() || taldaram->IsInEvadeMode())
						valid = false;
				if (Creature* valanar = instance->instance->GetCreature(instance->GetData64(DATA_PRINCE_VALANAR_GUID)))
					if (!valanar->IsAlive() || valanar->IsInEvadeMode())
						valid = false;
				if (!valid)
				{
					EnterEvadeMode();
					return;
				}

				instance->SetBossState(DATA_BLOOD_PRINCE_COUNCIL, IN_PROGRESS);
				instance->SetData(DATA_ORB_WHISPERER_ACHIEVEMENT, 1);
				me->RemoveAurasDueToSpell(SPELL_FEIGN_DEATH); // just in case
				me->setActive(true);
				DoZoneInCombat();
				if (!me->hasLootRecipient())
					me->SetLootRecipient(who);
				me->LowerPlayerDamageReq(me->GetMaxHealth());
				me->SetReactState(REACT_AGGRESSIVE);
				instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me);

				if (Creature* keleseth = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_PRINCE_KELESETH_GUID)))
					if (!keleseth->IsInEvadeMode())
						keleseth->SetInCombatWithZone();
				if (Creature* taldaram = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_PRINCE_TALDARAM_GUID)))
					if (!taldaram->IsInEvadeMode())
						taldaram->SetInCombatWithZone();

				DoAction(ACTION_CAST_INVOCATION);
				currentInvocationIndex = 0;
				invocationOrder[0] = DATA_PRINCE_VALANAR_GUID;
				invocationOrder[1] = RAND<uint32>(DATA_PRINCE_KELESETH_GUID, DATA_PRINCE_TALDARAM_GUID);
				invocationOrder[2] = DATA_PRINCE_KELESETH_GUID + DATA_PRINCE_TALDARAM_GUID - invocationOrder[1];

				events.ScheduleEvent(EVENT_BERSERK, 600000);
				events.ScheduleEvent(EVENT_KINETIC_BOMB, urand(18000, 24000));
				events.ScheduleEvent(EVENT_SHOCK_VORTEX, urand(15000, 20000));
				events.ScheduleEvent(EVENT_INVOCATION_OF_BLOOD, 45000);
				if (IsHeroic())
				{
					me->AddAura(SPELL_SHADOW_PRISON, me);
					me->CastSpell(me, SPELL_SHADOW_PRISON_DUMMY, true);
				}
			}

			void JustDied(Unit* /*killer*/)
			{
				events.Reset();
				summons.DespawnAll();
				instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);

				if (!_canDie)
				{
					me->RemoveCorpse(false);
					me->SetRespawnTime(10);
					me->SaveRespawnTime();
					Position homePos = me->GetHomePosition();
					me->UpdatePosition(homePos);
					me->GetMotionMaster()->Clear(false);
					me->GetMotionMaster()->MoveIdle();
					me->StopMovingOnCurrentPos();
					return;
				}

				Talk(SAY_VALANAR_DEATH);
				instance->SetBossState(DATA_BLOOD_PRINCE_COUNCIL, DONE);
				if (Creature* keleseth = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_PRINCE_KELESETH_GUID)))
					if (keleseth->IsAlive())
						Unit::Kill(keleseth, keleseth);
				if (Creature* taldaram = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_PRINCE_TALDARAM_GUID)))
					if (taldaram->IsAlive())
						Unit::Kill(taldaram, taldaram);
			}

			void JustRespawned()
			{
				BossAI::JustRespawned();
				JustReachedHome();
			}

			void JustReachedHome()
			{
				_canDie = true;
				me->setActive(false);
				instance->SetBossState(DATA_BLOOD_PRINCE_COUNCIL, FAIL);
				instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
				me->SetHealth(me->GetMaxHealth());
				DoAction(ACTION_CAST_INVOCATION);
			}

			void JustSummoned(Creature* summon)
			{
				summons.Summon(summon);
				switch (summon->GetEntry())
				{
					case NPC_KINETIC_BOMB_TARGET:
						summon->SetReactState(REACT_PASSIVE);
						summon->CastSpell(summon, SPELL_KINETIC_BOMB, true, NULL, NULL, me->GetGUID());
						break;
					case NPC_SHOCK_VORTEX:
						summon->m_Events.AddEvent(new ShockVortexExplodeEvent(*summon), summon->m_Events.CalculateTime(4500));
						break;
					default:
						break;
				}
			}

			void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType, SpellSchoolMask)
			{
				if (!_isEmpowered)
				{
					if (attacker)
						me->AddThreat(attacker, float(damage));
					damage = 0;
				}
			}

			void DamageDealt(Unit* target, uint32& damage, DamageEffectType damageType)
			{
				if (target->GetTypeId() != TYPEID_PLAYER)
					return;

				if (damage > RAID_MODE<uint32>(23000, 25000, 23000, 25000))
					instance->SetData(DATA_ORB_WHISPERER_ACHIEVEMENT, 0);
			}

			void KilledUnit(Unit* victim)
			{
				if (victim->GetTypeId() == TYPEID_PLAYER)
					Talk(SAY_VALANAR_KILL);
			}

			void SpellHit(Unit* /*caster*/, SpellInfo const* spell)
			{
				if (spell->Id == 71070 && me->IsInCombat() && !me->IsInEvadeMode())
					DoAction(ACTION_CAST_INVOCATION);
			}

			void DoAction(int32 action)
			{
				switch (action)
				{
					case ACTION_STAND_UP:
						summons.DespawnEntry(WORLD_TRIGGER);
						me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_29 | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC);
						me->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD);
						me->SetReactState(REACT_AGGRESSIVE);
						me->ForceValuesUpdateAtIndex(UNIT_NPC_FLAGS);   // was in sniff. don't ask why
						me->m_Events.AddEvent(new StandUpEvent(*me), me->m_Events.CalculateTime(1000));
						me->SetHealth(me->GetMaxHealth());
						DoAction(ACTION_CAST_INVOCATION);
						break;
					case ACTION_CAST_INVOCATION:
						if (me->IsInCombat())
							Talk(SAY_VALANAR_INVOCATION);
						me->CastSpell(me, SPELL_INVOCATION_OF_BLOOD_VALANAR, true);
						me->CastSpell(me, SPELL_INVOCATION_VISUAL_ACTIVE, true);
						_isEmpowered = true;
						break;
					case ACTION_REMOVE_INVOCATION:
						_isEmpowered = false;
						me->RemoveAurasDueToSpell(SPELL_INVOCATION_VISUAL_ACTIVE);
						me->RemoveAurasDueToSpell(SPELL_INVOCATION_OF_BLOOD_VALANAR);
						break;
					default:
						break;
				}
			}

			bool CheckRoom()
			{
				Creature* keleseth = instance->instance->GetCreature(instance->GetData64(DATA_PRINCE_KELESETH_GUID));
				Creature* taldaram = instance->instance->GetCreature(instance->GetData64(DATA_PRINCE_TALDARAM_GUID));
				if (keleseth && taldaram && CheckBoundary(me) && CheckBoundary(keleseth) && CheckBoundary(taldaram))
					return true;

				EnterEvadeMode();
				return false;
			}

			void UpdateAI(uint32 diff)
			{
				if (!UpdateVictim() || !CheckRoom())
					return;

				events.Update(diff);

				if (me->HasUnitState(UNIT_STATE_CASTING))
					return;

				switch (events.ExecuteEvent())
				{
					case EVENT_INVOCATION_OF_BLOOD:
						{
							uint32 visualSpellId = 0;
							Creature* current = instance->instance->GetCreature(instance->GetData64(invocationOrder[currentInvocationIndex]));
							if (++currentInvocationIndex >= 3)
								currentInvocationIndex = 0;
							Creature* next = instance->instance->GetCreature(instance->GetData64(invocationOrder[currentInvocationIndex]));
							switch (invocationOrder[currentInvocationIndex])
							{
								case DATA_PRINCE_KELESETH_GUID:
									visualSpellId = 71080;
									break;
								case DATA_PRINCE_TALDARAM_GUID:
									visualSpellId = 71081;
									break;
								case DATA_PRINCE_VALANAR_GUID:
									visualSpellId = 71070;
									break;
							}
							if (!visualSpellId || !current || !next || !current->IsInCombat() || !next->IsInCombat())
							{
								EnterEvadeMode();
								return;
							}
							next->SetHealth(current->GetHealth());
							current->AI()->DoAction(ACTION_REMOVE_INVOCATION);
							current->SetHealth(1);
							current->CastSpell((Unit*)NULL, visualSpellId, true);
							next->AI()->Talk(1);
						}
						events.ScheduleEvent(EVENT_INVOCATION_OF_BLOOD, 46000);
						break;
					case EVENT_BERSERK:
						me->CastSpell(me, SPELL_BERSERK, true);
						Talk(SAY_VALANAR_BERSERK);
						break;
					case EVENT_KINETIC_BOMB:
						if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true))
						{
							me->CastSpell(target, SPELL_KINETIC_BOMB_TARGET, false);
							Talk(SAY_VALANAR_SPECIAL);
						}
						events.ScheduleEvent(EVENT_KINETIC_BOMB, me->GetMap()->Is25ManRaid() ? 20500 : 30500);
						break;
					case EVENT_SHOCK_VORTEX:
						if (_isEmpowered)
						{
							me->CastSpell(me, SPELL_EMPOWERED_SHOCK_VORTEX, false);
							Talk(EMOTE_VALANAR_SHOCK_VORTEX);
							events.ScheduleEvent(EVENT_SHOCK_VORTEX, 30000);
						}
						else
						{
							if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true))
								me->CastSpell(target, SPELL_SHOCK_VORTEX, false);
							events.ScheduleEvent(EVENT_SHOCK_VORTEX, urand(18000, 23000));
						}
						break;
				}

				DoMeleeAttackIfReady();
			}

			void EnterEvadeMode()
			{
				if (_evading)
					return;
				_canDie = false;
				me->SetHealth(me->GetMaxHealth());
				DoAction(ACTION_REMOVE_INVOCATION);
				_evading = true;
				if (Creature* keleseth = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_PRINCE_KELESETH_GUID)))
					keleseth->AI()->EnterEvadeMode();
				if (Creature* taldaram = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_PRINCE_TALDARAM_GUID)))
					taldaram->AI()->EnterEvadeMode();
				BossAI::EnterEvadeMode();
				_evading = false;
			}
		};

		CreatureAI* GetAI(Creature* creature) const
		{
			return GetIcecrownCitadelAI<boss_prince_valanarAI>(creature);
		}
};

class npc_blood_queen_lana_thel : public CreatureScript
{
	public:
		npc_blood_queen_lana_thel() : CreatureScript("npc_blood_queen_lana_thel") { }

		struct npc_blood_queen_lana_thelAI : public ScriptedAI
		{
			npc_blood_queen_lana_thelAI(Creature* creature) : ScriptedAI(creature)
			{
				_introDone = false;
				_instance = creature->GetInstanceScript();
				me->m_SightDistance = 100.0f; // for MoveInLineOfSight distance
			}

			void Reset()
			{
				_events.Reset();
				me->SetDisableGravity(true);
				if (_instance->GetBossState(DATA_BLOOD_PRINCE_COUNCIL) == DONE)
				{
					me->SetVisible(false);
					_introDone = true;
				}
				else
					me->SetVisible(true);
			}

			void MoveInLineOfSight(Unit* who)
			{
				if (_introDone)
					return;

				if (who->GetTypeId() != TYPEID_PLAYER || me->GetExactDist2d(who) > 100.0f)
					return;

				_introDone = true;
				Talk(SAY_INTRO_1);
				_events.SetPhase(1);
				_events.ScheduleEvent(EVENT_INTRO_1, 14000);
				// summon a visual trigger
				if (Creature* summon = DoSummon(NPC_FLOATING_TRIGGER, triggerPos, 15000, TEMPSUMMON_TIMED_DESPAWN))
				{
					summon->SetDisplayId(11686); // it's a general npc, and the template has 2 models (first is an infernal)
					summon->CastSpell(summon, SPELL_OOC_INVOCATION_VISUAL, true);
					summon->SetSpeed(MOVE_RUN, 0.15f, true);
					summon->GetMotionMaster()->MovePoint(0, triggerEndPos);
				}
			}

			void MovementInform(uint32 type, uint32 id)
			{
				if (type == POINT_MOTION_TYPE && id == POINT_INTRO_DESPAWN)
					me->SetVisible(false);
			}

			void UpdateAI(uint32 diff)
			{
				if (!_events.GetPhaseMask())
					return;

				_events.Update(diff);

				if (_events.ExecuteEvent() == EVENT_INTRO_1)
				{
					Talk(SAY_INTRO_2);
					me->GetMotionMaster()->MovePoint(POINT_INTRO_DESPAWN, introFinalPos);
					_events.Reset();

					if (Creature* keleseth = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_PRINCE_KELESETH_GUID)))
						keleseth->AI()->DoAction(ACTION_STAND_UP);
					if (Creature* taldaram = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_PRINCE_TALDARAM_GUID)))
						taldaram->AI()->DoAction(ACTION_STAND_UP);
					if (Creature* valanar = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_PRINCE_VALANAR_GUID)))
						valanar->AI()->DoAction(ACTION_STAND_UP);
				}
			}

		private:
			EventMap _events;
			InstanceScript* _instance;
			bool _introDone;
		};

		CreatureAI* GetAI(Creature* creature) const
		{
			return GetIcecrownCitadelAI<npc_blood_queen_lana_thelAI>(creature);
		}
};

class npc_dark_nucleus : public CreatureScript
{
	public:
		npc_dark_nucleus() : CreatureScript("npc_dark_nucleus") { }

		struct npc_dark_nucleusAI : public ScriptedAI
		{
			npc_dark_nucleusAI(Creature* creature) : ScriptedAI(creature) {}

			uint16 timer;

			void Reset()
			{
				timer = 0;
				me->SetReactState(REACT_DEFENSIVE);
				me->SetHover(true);
				me->CastSpell(me, SPELL_SHADOW_RESONANCE_AURA, true);
			}

			void AttackStart(Unit* who)
			{
				if (who != me->GetVictim())
				{
					me->InterruptNonMeleeSpells(true, 0, true);
					me->CastSpell(who, SPELL_SHADOW_RESONANCE_RESIST, false);
					me->ClearUnitState(UNIT_STATE_CASTING);
				}
				ScriptedAI::AttackStart(who);
			}

			void DamageTaken(Unit* attacker, uint32& /*damage*/, DamageEffectType det, SpellSchoolMask)
			{
				if (!attacker || attacker == me || attacker == me->GetVictim() || (det != DIRECT_DAMAGE && det != SPELL_DIRECT_DAMAGE))
					return;

				me->DeleteThreatList();
				me->AddThreat(attacker, 500000000.0f);
			}

			void JustDied(Unit* /*killer*/)
			{
				me->DespawnOrUnsummon(1);
			}

			void UpdateAI(uint32 diff)
			{
				if (!UpdateVictim())
					return;

				if (timer <= diff)
				{
					timer = 1000;
					if (Unit* victim = me->GetVictim())
						if (me->GetDistance(victim) < 15.0f && !victim->HasAura(SPELL_SHADOW_RESONANCE_RESIST, me->GetGUID()))
						{
							me->InterruptNonMeleeSpells(true, 0, true);
							me->CastSpell(victim, SPELL_SHADOW_RESONANCE_RESIST, false);
							me->ClearUnitState(UNIT_STATE_CASTING);
						}
				}
				else
					timer -= diff;
			}
		};

		CreatureAI* GetAI(Creature* creature) const
		{
			return GetIcecrownCitadelAI<npc_dark_nucleusAI>(creature);
		}
};

class npc_ball_of_flame : public CreatureScript
{
	public:
		npc_ball_of_flame() : CreatureScript("npc_ball_of_flame") { }

		struct npc_ball_of_flameAI : public ScriptedAI
		{
			npc_ball_of_flameAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript())
			{
				_chaseGUID = 0;
				_exploded = false;
				_started = false;
				if (me->GetEntry() == NPC_BALL_OF_INFERNO_FLAME)
					me->CastSpell(me, SPELL_BALL_OF_FLAMES_PROC, true);
				me->NearTeleportTo(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()+5.0f, me->GetOrientation());
			}

			InstanceScript* _instance;
			uint64 _chaseGUID;
			bool _exploded;
			bool _started;

			void AttackStart(Unit* who)
			{
				if (_started)
					ScriptedAI::AttackStart(who);
			}

			void MoveInLineOfSight(Unit* /*who*/) {}

			void MovementInform(uint32 type, uint32 id)
			{
				if (type == CHASE_MOTION_TYPE && !_exploded)
				{
					me->RemoveAurasDueToSpell(SPELL_BALL_OF_FLAMES_PERIODIC);
					me->SetReactState(REACT_PASSIVE);
					me->SetControlled(true, UNIT_STATE_ROOT);
					me->StopMoving();
					me->CastSpell(me, SPELL_FLAMES, true);
					me->DespawnOrUnsummon(999);
					me->CastSpell(me, SPELL_FLAME_SPHERE_DEATH_EFFECT, true);
					_exploded = true;
				}
			}

			void SetGUID(uint64 guid, int32 /*type*/)
			{
				_chaseGUID = guid;
			}

			void DoAction(int32 action)
			{
				if (action != ACTION_FLAME_BALL_CHASE || me->IsInCombat())
					return;
				Player* target = NULL;
				if (_chaseGUID)
					target = ObjectAccessor::GetPlayer(*me, _chaseGUID);
				if (!target)
					target = ScriptedAI::SelectTargetFromPlayerList(150.0f, 0, true);
				if (target)
				{
					// need to clear states now because this call is before AuraEffect is fully removed
					_started = true;
					if (me->GetEntry() == NPC_BALL_OF_INFERNO_FLAME)
						me->CastSpell(me, SPELL_BALL_OF_FLAMES_PERIODIC, true);
					me->ClearUnitState(UNIT_STATE_CASTING | UNIT_STATE_STUNNED);
					me->AddThreat(target, 1000.0f);
					me->SetCanFly(true);
					me->SetDisableGravity(true);
					me->SetHover(true);
					me->SendMovementFlagUpdate();
					AttackStart(target);
					me->SetInCombatWithZone();
					return;
				}
				me->DespawnOrUnsummon(1);
			}

			void DamageDealt(Unit* target, uint32& damage, DamageEffectType damageType)
			{
				if (target->GetTypeId() != TYPEID_PLAYER)
					return;

				if (damage > RAID_MODE<uint32>(23000, 25000, 23000, 25000))
					_instance->SetData(DATA_ORB_WHISPERER_ACHIEVEMENT, 0);
			}
		};

		CreatureAI* GetAI(Creature* creature) const
		{
			return GetIcecrownCitadelAI<npc_ball_of_flameAI>(creature);
		}
};

class npc_kinetic_bomb : public CreatureScript
{
	public:
		npc_kinetic_bomb() : CreatureScript("npc_kinetic_bomb") { }

		struct npc_kinetic_bombAI : public NullCreatureAI
		{
			npc_kinetic_bombAI(Creature* creature) : NullCreatureAI(creature)
			{
			}

			EventMap _events;
			float _x;
			float _y;
			float _groundZ;
			bool exploded;

			void IsSummonedBy(Unit* /*summoner*/)
			{
				if (InstanceScript* instance = me->GetInstanceScript())
					if (Creature* valanar = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_PRINCE_VALANAR_GUID)))
						valanar->AI()->JustSummoned(me);
			}

			void Reset()
			{
				_events.Reset();
				_events.RescheduleEvent(EVENT_BOMB_DESPAWN, 60000);
				me->SetWalk(true);
				exploded = false;

				float x, y, z;
				me->GetPosition(x, y, z);
				_x = x;
				_y = y;
				_groundZ = me->GetMap()->GetHeight(me->GetPhaseMask(), x, y, z, true, 500.0f);
				me->GetMotionMaster()->MoveCharge(_x, _y, _groundZ, me->GetSpeed(MOVE_WALK));
			}

			void DoAction(int32 action)
			{
				if (action == SPELL_KINETIC_BOMB_EXPLOSION)
				{
					exploded = true;
					_events.RescheduleEvent(EVENT_BOMB_DESPAWN, 1000);
				}
				else if (action == ACTION_KINETIC_BOMB_JUMP)
				{
					if (!me->HasAura(SPELL_KINETIC_BOMB_KNOCKBACK))
					{
						me->GetMotionMaster()->MovementExpired(false);
						me->StopMoving();
						me->GetMotionMaster()->MoveCharge(_x, _y, me->GetPositionZ() + 60.0f, me->GetSpeed(MOVE_RUN));
					}
					_events.RescheduleEvent(EVENT_CONTINUE_FALLING, 3000);
				}
			}

			void UpdateAI(uint32 diff)
			{
				_events.Update(diff);
				switch (_events.ExecuteEvent())
				{
					case EVENT_BOMB_DESPAWN:
						me->RemoveAllAuras();
						me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
						me->DespawnOrUnsummon(exploded ? 5000 : 0);
						break;
					case EVENT_CONTINUE_FALLING:
						me->GetMotionMaster()->MovementExpired(false);
						me->StopMoving();
						me->GetMotionMaster()->MoveCharge(_x, _y, _groundZ, me->GetSpeed(MOVE_WALK));
						break;
				}
			}
		};

		CreatureAI* GetAI(Creature* creature) const
		{
			return GetIcecrownCitadelAI<npc_kinetic_bombAI>(creature);
		}
};

class spell_blood_council_shadow_prison : public SpellScriptLoader
{
	public:
		spell_blood_council_shadow_prison() : SpellScriptLoader("spell_blood_council_shadow_prison") { }

		class spell_blood_council_shadow_prison_AuraScript : public AuraScript
		{
			PrepareAuraScript(spell_blood_council_shadow_prison_AuraScript);

			void HandleDummyTick(AuraEffect const* aurEff)
			{
				if (GetTarget()->GetTypeId() == TYPEID_PLAYER && GetTarget()->isMoving())
					GetTarget()->CastSpell(GetTarget(), SPELL_SHADOW_PRISON_DAMAGE, true, NULL, aurEff);
			}

			void Register()
			{
				OnEffectPeriodic += AuraEffectPeriodicFn(spell_blood_council_shadow_prison_AuraScript::HandleDummyTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
			}
		};

		AuraScript* GetAuraScript() const
		{
			return new spell_blood_council_shadow_prison_AuraScript();
		}
};

class spell_blood_council_shadow_prison_damage : public SpellScriptLoader
{
	public:
		spell_blood_council_shadow_prison_damage() : SpellScriptLoader("spell_blood_council_shadow_prison_damage") { }

		class spell_blood_council_shadow_prison_SpellScript : public SpellScript
		{
			PrepareSpellScript(spell_blood_council_shadow_prison_SpellScript);

			void AddExtraDamage()
			{
				if (Aura* aur = GetHitUnit()->GetAura(GetSpellInfo()->Id))
					if (AuraEffect const* eff = aur->GetEffect(EFFECT_1))
						SetHitDamage(GetHitDamage() + eff->GetAmount());
			}

			void Register()
			{
				OnHit += SpellHitFn(spell_blood_council_shadow_prison_SpellScript::AddExtraDamage);
			}
		};

		SpellScript* GetSpellScript() const
		{
			return new spell_blood_council_shadow_prison_SpellScript();
		}
};

class spell_taldaram_glittering_sparks : public SpellScriptLoader
{
	public:
		spell_taldaram_glittering_sparks() : SpellScriptLoader("spell_taldaram_glittering_sparks") { }

		class spell_taldaram_glittering_sparks_SpellScript : public SpellScript
		{
			PrepareSpellScript(spell_taldaram_glittering_sparks_SpellScript);

			void HandleScript(SpellEffIndex effIndex)
			{
				PreventHitDefaultEffect(effIndex);
				GetCaster()->CastSpell(GetCaster(), uint32(GetEffectValue()), true);
			}

			void Register()
			{
				OnEffectHitTarget += SpellEffectFn(spell_taldaram_glittering_sparks_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
			}
		};

		SpellScript* GetSpellScript() const
		{
			return new spell_taldaram_glittering_sparks_SpellScript();
		}
};

class spell_taldaram_summon_flame_ball : public SpellScriptLoader
{
	public:
		spell_taldaram_summon_flame_ball() : SpellScriptLoader("spell_taldaram_summon_flame_ball") { }

		class spell_taldaram_summon_flame_ball_SpellScript : public SpellScript
		{
			PrepareSpellScript(spell_taldaram_summon_flame_ball_SpellScript);

			bool Load()
			{
				if (GetCaster()->GetTypeId() != TYPEID_UNIT)
					return false;
				GetCaster()->CastSpell(GetCaster(), uint32(GetSpellInfo()->Effects[0].CalcValue()), true);
				return true;
			}

			void HandleScript(SpellEffIndex effIndex)
			{
				PreventHitDefaultEffect(effIndex);
				GetCaster()->ToCreature()->AI()->DoAction(ACTION_FLAME_BALL_CHASE);
			}

			void Register()
			{
				OnEffectHitTarget += SpellEffectFn(spell_taldaram_summon_flame_ball_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY);
			}
		};

		SpellScript* GetSpellScript() const
		{
			return new spell_taldaram_summon_flame_ball_SpellScript();
		}
};

class spell_taldaram_ball_of_inferno_flame : public SpellScriptLoader
{
	public:
		spell_taldaram_ball_of_inferno_flame() : SpellScriptLoader("spell_taldaram_ball_of_inferno_flame") { }

		class spell_taldaram_ball_of_inferno_flame_SpellScript : public SpellScript
		{
			PrepareSpellScript(spell_taldaram_ball_of_inferno_flame_SpellScript);

			void ModAuraStack()
			{
				if (Aura* aur = GetHitAura())
					aur->SetStackAmount(uint8(GetSpellInfo()->StackAmount));
			}

			void Register()
			{
				AfterHit += SpellHitFn(spell_taldaram_ball_of_inferno_flame_SpellScript::ModAuraStack);
			}
		};

		SpellScript* GetSpellScript() const
		{
			return new spell_taldaram_ball_of_inferno_flame_SpellScript();
		}
};

class spell_valanar_kinetic_bomb : public SpellScriptLoader
{
	public:
		spell_valanar_kinetic_bomb() : SpellScriptLoader("spell_valanar_kinetic_bomb") { }

		class spell_valanar_kinetic_bomb_SpellScript : public SpellScript
		{
			PrepareSpellScript(spell_valanar_kinetic_bomb_SpellScript);

			void ChangeSummonPos(SpellEffIndex /*effIndex*/)
			{
				WorldLocation summonPos = *GetExplTargetDest();
				Position offset = {0.0f, 0.0f, 20.0f, 0.0f};
				summonPos.RelocateOffset(offset);
				SetExplTargetDest(summonPos);
				GetHitDest()->RelocateOffset(offset);
			}

			void Register()
			{
				OnEffectHit += SpellEffectFn(spell_valanar_kinetic_bomb_SpellScript::ChangeSummonPos, EFFECT_0, SPELL_EFFECT_SUMMON);
			}
		};

		class spell_valanar_kinetic_bomb_AuraScript : public AuraScript
		{
			PrepareAuraScript(spell_valanar_kinetic_bomb_AuraScript);

			void HandleDummyTick(AuraEffect const* /*aurEff*/)
			{
				Unit* target = GetTarget();
				if (target->GetTypeId() != TYPEID_UNIT)
					return;

				if (Creature* bomb = target->FindNearestCreature(NPC_KINETIC_BOMB, 1.0f, true))
				{
					bomb->CastSpell(bomb, SPELL_KINETIC_BOMB_EXPLOSION, true);
					bomb->RemoveAurasDueToSpell(SPELL_KINETIC_BOMB_VISUAL);
					target->RemoveAura(GetAura());
					bomb->AI()->DoAction(SPELL_KINETIC_BOMB_EXPLOSION);
				}
			}

			void Register()
			{
				OnEffectPeriodic += AuraEffectPeriodicFn(spell_valanar_kinetic_bomb_AuraScript::HandleDummyTick, EFFECT_1, SPELL_AURA_PERIODIC_DUMMY);
			}
		};

		SpellScript* GetSpellScript() const
		{
			return new spell_valanar_kinetic_bomb_SpellScript();
		}

		AuraScript* GetAuraScript() const
		{
			return new spell_valanar_kinetic_bomb_AuraScript();
		}
};

class spell_valanar_kinetic_bomb_absorb : public SpellScriptLoader
{
	public:
		spell_valanar_kinetic_bomb_absorb() : SpellScriptLoader("spell_valanar_kinetic_bomb_absorb") { }

		class spell_valanar_kinetic_bomb_absorb_AuraScript : public AuraScript
		{
			PrepareAuraScript(spell_valanar_kinetic_bomb_absorb_AuraScript);

			void OnAbsorb(AuraEffect* aurEff, DamageInfo& dmgInfo, uint32& absorbAmount)
			{
				absorbAmount = CalculatePct(dmgInfo.GetDamage(), aurEff->GetAmount());
				RoundToInterval<uint32>(absorbAmount, 0, dmgInfo.GetDamage());
				dmgInfo.AbsorbDamage(absorbAmount);
			}

			void Register()
			{
				OnEffectAbsorb += AuraEffectAbsorbFn(spell_valanar_kinetic_bomb_absorb_AuraScript::OnAbsorb, EFFECT_0);
			}
		};

		AuraScript* GetAuraScript() const
		{
			return new spell_valanar_kinetic_bomb_absorb_AuraScript();
		}
};

class spell_valanar_kinetic_bomb_knockback : public SpellScriptLoader
{
	public:
		spell_valanar_kinetic_bomb_knockback() : SpellScriptLoader("spell_valanar_kinetic_bomb_knockback") { }

		class spell_valanar_kinetic_bomb_knockback_SpellScript : public SpellScript
		{
			PrepareSpellScript(spell_valanar_kinetic_bomb_knockback_SpellScript);

			void KnockIntoAir()
			{
				if (Creature* target = GetHitCreature())
					target->AI()->DoAction(ACTION_KINETIC_BOMB_JUMP);
			}

			void Register()
			{
				BeforeHit += SpellHitFn(spell_valanar_kinetic_bomb_knockback_SpellScript::KnockIntoAir);
			}
		};

		SpellScript* GetSpellScript() const
		{
			return new spell_valanar_kinetic_bomb_knockback_SpellScript();
		}
};

class spell_valanar_kinetic_bomb_summon : public SpellScriptLoader
{
	public:
		spell_valanar_kinetic_bomb_summon() : SpellScriptLoader("spell_valanar_kinetic_bomb_summon") { }

		class spell_valanar_kinetic_bomb_summon_SpellScript : public SpellScript
		{
			PrepareSpellScript(spell_valanar_kinetic_bomb_summon_SpellScript);

			void SelectDest()
			{
				if (Position* dest = const_cast<WorldLocation*>(GetExplTargetDest()))
				{
					float angle = dest->GetAngle(GetCaster());
					Position offset = {6.0f*cos(angle), 6.0f*sin(angle), 10.0f, 0.0f};
					dest->RelocateOffset(offset);
					GetCaster()->UpdateAllowedPositionZ(dest->GetPositionX(), dest->GetPositionY(), dest->m_positionZ);
				}
			}

			void Register()
			{
				BeforeCast += SpellCastFn(spell_valanar_kinetic_bomb_summon_SpellScript::SelectDest);
			}

			uint32 _targetCount;
		};

		SpellScript* GetSpellScript() const
		{
			return new spell_valanar_kinetic_bomb_summon_SpellScript();
		}
};

class spell_blood_council_summon_shadow_resonance : public SpellScriptLoader
{
    public:
        spell_blood_council_summon_shadow_resonance() : SpellScriptLoader("spell_blood_council_summon_shadow_resonance") { }

        class spell_blood_council_summon_shadow_resonance_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_blood_council_summon_shadow_resonance_SpellScript);

            void SetDest(SpellDestination& dest)
            {
				Unit* summoner = GetCaster();
				float x = dest._position.GetPositionX();
				float y = dest._position.GetPositionY();
				float angle = summoner->GetAngle(x, y);
				if (dest._position.GetExactDist2d(summoner) > 35.0f && x > 4585.0f && y > 2716.0f && y < 2822.0f)
					return;

				for (uint8 a=0; a<2; ++a)
					for (uint8 i=6; i>0; --i)
					{
						float destX = summoner->GetPositionX()+cos(angle + a*M_PI)*i*10.0f;
						float destY = summoner->GetPositionY()+sin(angle + a*M_PI)*i*10.0f;
						if (summoner->GetMap()->isInLineOfSight(summoner->GetPositionX(), summoner->GetPositionY(), summoner->GetPositionZ()+10.0f, destX, destY, summoner->GetPositionZ()+10.0f, summoner->GetPhaseMask()) && destX > 4585.0f && destY > 2716.0f && destY < 2822.0f)
						{
							float destZ = summoner->GetMap()->GetHeight(summoner->GetPhaseMask(), destX, destY, summoner->GetPositionZ()+10.0f);
							if (fabs(destZ-summoner->GetPositionZ()) < 10.0f) // valid z found
							{
								dest._position.Relocate(destX, destY, destZ);
								return;
							}
						}
					}

				dest._position.Relocate(summoner->GetPositionX(), summoner->GetPositionY(), summoner->GetPositionZ());
            }

            void Register()
            {
                OnDestinationTargetSelect += SpellDestinationTargetSelectFn(spell_blood_council_summon_shadow_resonance_SpellScript::SetDest, EFFECT_0, TARGET_DEST_CASTER_RANDOM);
            }
        };

        SpellScript* GetSpellScript() const
        {
            return new spell_blood_council_summon_shadow_resonance_SpellScript();
        }
};

void AddSC_boss_blood_prince_council()
{
	new boss_prince_keleseth_icc();
	new boss_prince_taldaram_icc();
	new boss_prince_valanar_icc();
	new npc_blood_queen_lana_thel();
	new npc_dark_nucleus();
	new npc_ball_of_flame();
	new npc_kinetic_bomb();
	new spell_blood_council_shadow_prison();
	new spell_blood_council_shadow_prison_damage();
	new spell_taldaram_glittering_sparks();
	new spell_taldaram_summon_flame_ball();
	new spell_taldaram_ball_of_inferno_flame();
	new spell_valanar_kinetic_bomb();
	new spell_valanar_kinetic_bomb_absorb();
	new spell_valanar_kinetic_bomb_knockback();
	new spell_valanar_kinetic_bomb_summon();
	new spell_blood_council_summon_shadow_resonance();
}